<template>
	<div class="form-item-datetime">
		<van-field :label="label" :required="required" :disabled="disabled" :model-value="displayText" :readonly="readonly" @click="open" />
		<van-popup v-model:show="show" position="bottom" :style="{ height: '50%' }">
			<div class="picker-header">
				<span class="van-picker__cancel" @click="onCancel">取消</span>
				<div class="title">{{ label || '选择时间' }}</div>
				<span class="van-picker__confirm" @click="onNow">现在</span>
			</div>
			<div class="dual-box">
				<van-date-picker
					class="date-picker"
					v-model="dateValues"
					:columns-type="['year', 'month', 'day']"
					:formatter="dateFormatter"
					:show-toolbar="false"
					:min-date="minDateObj"
					:max-date="maxDateObj"
					@change="onDateChange"
				/>
				<van-time-picker
					v-if="precision !== 'day'"
					class="time-picker"
					v-model="timeValues"
					:columns-type="timeColumns"
					:formatter="timeFormatter"
					:show-toolbar="false"
					:filter="onTimeFilter"
					@change="onTimeChange"
				/>
			</div>
			<!-- 底部原确认按钮移除 -->
			<div class="footer-btns">
				<van-button block type="primary" @click="onConfirm">确定</van-button>
			</div>
		</van-popup>
	</div>
</template>

<script>
import { ref, watch, computed } from 'vue'
import dayjs from 'dayjs'

export default {
	name: 'FormItemDateTime',
	props: {
		modelValue: { type: String, default: '' },
		itemKey: { type: String, default: '' },
		label: { type: String, default: '' },
		required: { type: Boolean, default: false },
		disabled: { type: Boolean, default: false },
		readonly: { type: Boolean, default: false },
		formatter: { type: String, default: 'YYYY-MM-DD HH:mm' },
		precision: { type: String, default: 'minute' },
		startYear: { type: Number, default: 2020 },
		endYear: { type: Number, default: () => new Date().getFullYear() + 5 },
		useTSep: { type: Boolean, default: false },
		placeholder: { type: String, default: '请选择' },
		// 最小/最大限制（可为时间戳/字符串/Date）
		minDate: { type: [Number, String, Date], default: null },
		maxDate: { type: [Number, String, Date], default: null },
	},
	emits: ['update:modelValue', 'confirm', 'change'],
	setup(props, { emit }) {
		const show = ref(false)
		const originalValue = ref(props.modelValue)
		// dateValues: [year, month, day]; timeValues 根据 precision
		const dateValues = ref([dayjs().format('YYYY'), dayjs().format('MM'), dayjs().format('DD')])
		const timeValues = ref(initTimeValues())

		// ====== 时间上下限计算（需在 watcher 与 clamp 前定义） ======
		const hourBounds = computed(() => {
			if (!props.minDate && !props.maxDate) return { min: 0, max: 23 }
			const currentDay = dayjs(dateValues.value.join('-'))
			let min = 0,
				max = 23
			if (props.minDate) {
				const md = dayjs(props.minDate)
				if (currentDay.isSame(md, 'day')) min = md.hour()
			}
			if (props.maxDate) {
				const xd = dayjs(props.maxDate)
				if (currentDay.isSame(xd, 'day')) max = xd.hour()
			}
			return { min, max }
		})

		const minuteBounds = computed(() => {
			if (!props.minDate && !props.maxDate) return { min: 0, max: 59 }
			if (props.precision !== 'minute') return { min: 0, max: 59 }
			const currentDayHour = dayjs(`${dateValues.value.join('-')} ${timeValues.value[0] || '00'}`)
			let min = 0,
				max = 59
			if (props.minDate) {
				const md = dayjs(props.minDate)
				if (currentDayHour.isSame(md, 'hour')) min = md.minute()
			}
			if (props.maxDate) {
				const xd = dayjs(props.maxDate)
				if (currentDayHour.isSame(xd, 'hour')) max = xd.minute()
			}
			return { min, max }
		})

		function clampTimeValues() {
			if (props.precision === 'day') return
			// hour clamp
			if (timeValues.value.length > 0) {
				const { min: hMin, max: hMax } = hourBounds.value
				let h = Number(timeValues.value[0])
				if (h < hMin) h = hMin
				if (h > hMax) h = hMax
				timeValues.value[0] = String(h).padStart(2, '0')
			}
			// minute clamp
			if (props.precision === 'minute' && timeValues.value.length > 1) {
				const { min: mMin, max: mMax } = minuteBounds.value
				let m = Number(timeValues.value[1])
				if (m < mMin) m = mMin
				if (m > mMax) m = mMax
				timeValues.value[1] = String(m).padStart(2, '0')
			}
		}

		function initTimeValues() {
			if (props.precision === 'day') return []
			if (props.precision === 'hour') return [dayjs().format('HH')]
			return [dayjs().format('HH'), dayjs().format('mm')] // minute
		}

		function parseInput(val) {
			if (!val) return null
			const norm = val.replace('T', ' ')
			const fmts = ['YYYY-MM-DD HH:mm', 'YYYY-MM-DD HH:mm:ss']
			for (const f of fmts) {
				const d = dayjs(norm, f, true)
				if (d.isValid()) return d
			}
			const d2 = dayjs(norm)
			return d2.isValid() ? d2 : null
		}

		watch(
			() => props.modelValue,
			(nv) => {
				const d = parseInput(nv) || dayjs()
				dateValues.value = [d.format('YYYY'), d.format('MM'), d.format('DD')]
				if (props.precision === 'day') timeValues.value = []
				else if (props.precision === 'hour') timeValues.value = [d.format('HH')]
				else timeValues.value = [d.format('HH'), d.format('mm')]
				clampTimeValues()
			},
			{ immediate: true }
		)

		// 当 minDate/maxDate 动态变化时重新 clamp 当前值
		watch(
			() => [props.minDate, props.maxDate],
			() => {
				if (!props.modelValue) return
				const d = parseInput(props.modelValue)
				if (!d) return
				const clamped = clampDate(d)
				if (!clamped.isSame(d)) {
					dateValues.value = [clamped.format('YYYY'), clamped.format('MM'), clamped.format('DD')]
					if (props.precision === 'day') timeValues.value = []
					else if (props.precision === 'hour') timeValues.value = [clamped.format('HH')]
					else timeValues.value = [clamped.format('HH'), clamped.format('mm')]
					clampTimeValues()
					emit('update:modelValue', clamped.format(props.formatter))
				}
			},
			{ flush: 'post' }
		)

		const open = () => {
			if (props.disabled || props.readonly) return
			originalValue.value = props.modelValue
			show.value = true
		}
		const close = () => {
			show.value = false
		}

		const timeColumns = computed(() => {
			if (props.precision === 'day') return []
			if (props.precision === 'hour') return ['hour']
			return ['hour', 'minute']
		})

		const minDateObj = computed(() => (props.minDate ? dayjs(props.minDate).toDate() : undefined))
		const maxDateObj = computed(() => (props.maxDate ? dayjs(props.maxDate).toDate() : undefined))

		const dateFormatter = (type, option) => {
			if (type === 'year') option.text += '年'
			if (type === 'month') option.text += '月'
			if (type === 'day') option.text += '日'
			return option
		}
		const timeFormatter = (type, option) => {
			if (type === 'hour') option.text += '时'
			if (type === 'minute') option.text += '分'
			if (type === 'second') option.text += '秒'
			return option
		}

		const onTimeFilter = (type, options) => {
			if (type === 'hour') {
				const { min, max } = hourBounds.value
				return options.filter((o) => Number(o.value) >= min && Number(o.value) <= max)
			}
			if (type === 'minute') {
				const { min, max } = minuteBounds.value
				return options.filter((o) => Number(o.value) >= min && Number(o.value) <= max)
			}
			return options
		}

		const onDateChange = ({ selectedValues }) => {
			dateValues.value = selectedValues
			// 日期变化后重新校正时间列
			clampTimeValues()
			autoCommit()
		}
		const onTimeChange = ({ selectedValues }) => {
			timeValues.value = selectedValues
			autoCommit()
		}

		function clampDate(dt) {
			let ret = dt
			if (props.minDate) {
				const md = dayjs(props.minDate)
				if (ret.isBefore(md)) ret = md
			}
			if (props.maxDate) {
				const xd = dayjs(props.maxDate)
				if (ret.isAfter(xd)) ret = xd
			}
			return ret
		}
		const buildDateTime = () => {
			const [y, m, d] = dateValues.value
			let raw = `${y}-${m}-${d}`
			if (props.precision !== 'day') {
				const h = timeValues.value[0]
				raw += `${props.useTSep ? 'T' : ' '}${h}`
				if (props.precision === 'minute') raw += `:${timeValues.value[1]}`
			}
			let dt = dayjs(raw.replace('T', ' '))
			dt = clampDate(dt)
			// 若 clamp 发生，同步回选中列的值
			dateValues.value = [dt.format('YYYY'), dt.format('MM'), dt.format('DD')]
			if (props.precision === 'hour') timeValues.value = [dt.format('HH')]
			else if (props.precision === 'minute') timeValues.value = [dt.format('HH'), dt.format('mm')]
			clampTimeValues()
			return dt.format(props.formatter)
		}

		function autoCommit() {
			const formatted = buildDateTime()
			emit('update:modelValue', formatted)
			emit('change', { [props.itemKey]: formatted })
		}

		const useNow = () => {
			let d = dayjs()
			d = clampDate(d)
			dateValues.value = [d.format('YYYY'), d.format('MM'), d.format('DD')]
			if (props.precision === 'day') timeValues.value = []
			else if (props.precision === 'hour') timeValues.value = [d.format('HH')]
			else timeValues.value = [d.format('HH'), d.format('mm')]
		}

		const onNow = () => {
			useNow()
			autoCommit()
			emit('confirm', { [props.itemKey]: buildDateTime() })
			close()
		}
		const onCancel = () => {
			emit('update:modelValue', originalValue.value)
			close()
		}
		const onConfirm = () => {
			emit('confirm', { [props.itemKey]: buildDateTime() })
			autoCommit()
			close()
		}

		const displayText = computed(() => {
			if (!props.modelValue) return props.placeholder
			const d = parseInput(props.modelValue)
			return d ? d.format(props.formatter) : props.modelValue
		})

		return {
			show,
			open,
			close,
			dateValues,
			timeValues,
			timeColumns,
			minDateObj,
			maxDateObj,
			dateFormatter,
			timeFormatter,
			onTimeFilter,
			onDateChange,
			onTimeChange,
			useNow,
			displayText,
			precision: props.precision,
			onNow,
			onCancel,
			onConfirm,
		}
	},
}
</script>

<style lang="less" scoped>
.picker-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 8px 16px;
	font-size: 14px;
}
.picker-header .title {
	font-weight: 600;
}
.picker-header .van-picker__cancel,
.picker-header .van-picker__confirm {
	color: #1989fa;
	font-size: 14px;
	padding: 4px 8px;
	cursor: pointer;
}
.picker-header .van-picker__cancel {
	color: #646566;
}
.dual-box {
	display: flex;
}
.date-picker {
	flex: 2;
}
.time-picker {
	flex: 1;
}
.footer-btns {
	padding: 12px;
}
</style>